﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Net;
using LinqToBing.Bing.Wrapper.Common;
using LinqToBing.Bing.Wrapper.Enums;
using LinqToBing.Bing.Wrapper.ExtensionMethods;
using LinqToBing.Bing.Wrapper.Interfaces;
using LinqToBing.Bing.Wrapper.Search;
using LinqToBing.Bing.Wrapper.Exceptions;

namespace LinqToBing.Bing.Wrapper
{
    /// <summary>
    /// This is one of the most important class of the wrapper. It concretely executes the searches.
    /// </summary>
    /// <remarks>It consists of a lot of static methods.</remarks>
    public class SearchHelper
    {
        /// <summary>
        /// Private static attribute for defined Application Id.
        /// </summary>
        private static string appId = string.Empty;
        /// Private static property to define Application Id.
        /// Application Id must be registered on Bing API developer portal.
        /// This Application Id will be used for all searches.
        private static string AppId
        {
            get { return appId; }
            set
            {
                if (appId != value)
                {
                    if (value.IsValidAppId())
                    {
                        appId = value;
                    }
                    else
                    {
                        throw new InvalidAppIdException(value);
                    }
                }
            }
        }

        /// <summary>
        /// Register for specific Application Id to use in every search request
        /// </summary>
        /// <param name="AppId"></param>
        public static void RegisterAppId(string AppId)
        {
            if (string.IsNullOrEmpty(AppId))
            {
                throw new InvalidAppIdException();
            }
            else
            {
                if (!AppId.IsValidAppId())
                {
                    throw new InvalidAppIdException(AppId);
                }
                else
                {
                    SearchHelper.AppId = AppId;
                }
            }
        }

        #region DoSearch
        /// <summary>
        /// Execute the search request to obtain results.
        /// </summary>
        /// <param name="SearchTypes">Array of desired search types.</param>
        /// <param name="SearchQuery">The query to search for.</param>
        /// <param name="Offset">Offset value (whens is the same for all SearchTypes).</param>
        /// <param name="Count">Count value (whens is the same for all SearchTypes).</param>
        /// <returns>Returns an XDocument with results obtained.</returns>
        /// <remarks>This overload uses Application Id registered in the static property AppId.</remarks>
        internal static XDocument DoSearch(SearchSource[] SearchTypes, string SearchQuery, int? Offset, int? Count)
        {
            return DoSearch(SearchHelper.AppId, SearchTypes, SearchQuery, Offset, Count);
        }
        /// <summary>
        /// Execute the search request to obtain results.
        /// </summary>
        /// <param name="AppId">Application Id registered on Bing portal.</param>
        /// <param name="SearchTypes">Array of desired search types.</param>
        /// <param name="SearchQuery">The query to search for.</param>
        /// <param name="Offset">Offset value (whens is the same for all SearchTypes).</param>
        /// <param name="Count">Count value (whens is the same for all SearchTypes).</param>
        /// <returns>Returns an XDocument with results obtained.</returns>
        internal static XDocument DoSearch(string AppId, SearchSource[] SearchTypes, string SearchQuery, int? Offset, int? Count)
        {
            XDocument Result = null;
            if ((SearchTypes == null) || (SearchTypes.Length == 0))
            {
                throw new MissingSearchSourceException();
            }
            else
            {
                SearchTypeContainer[] searchTypeContainer = new SearchTypeContainer[SearchTypes.Length];
                for (int i = 0; i < SearchTypes.Length; i++)
                {
                    searchTypeContainer[i] = SearchTypeContainer.CreateContainer(SearchTypes[i], Offset, Count);
                }
                Result = DoSearch(AppId, searchTypeContainer, SearchQuery);
            }
            return Result;
        }

        internal static XDocument DoSearch(string AppId, List<SearchSource> SearchTypes, string SearchQuery, int? Offset, int? Count)
        {
            XDocument Result = null;
            if ((SearchTypes == null) || (SearchTypes.Count == 0))
            {
                throw new MissingSearchSourceException();
            }
            else
            {
                Result = DoSearch(AppId, SearchTypes.ToArray<SearchSource>(), SearchQuery, Offset, Count);
            }
            return Result;
        }
        internal static XDocument DoSearch(SearchTypeContainer[] SearchTypesContainer, string SearchQuery)
        {
            return DoSearch(SearchHelper.AppId, SearchTypesContainer, SearchQuery);
        }
        internal static XDocument DoSearch(string AppId, SearchTypeContainer[] SearchTypesContainer, string SearchQuery)
        {
            return DoSearch(AppId, SearchTypesContainer, SearchQuery, null);
        }
        internal static XDocument DoSearch(SearchTypeContainer[] SearchTypesContainer, string SearchQuery, Dictionary<string, object> Params)
        {
            return DoSearch(SearchHelper.AppId, SearchTypesContainer, SearchQuery, Params);
        }
        internal static XDocument DoSearch(string AppId, SearchTypeContainer[] SearchTypesContainer, string SearchQuery, Dictionary<string, object> Params)
        {
            string SearchSources = SearchTypesContainer.GetSourcesPreparedForSearch();
            StringBuilder sbFullApiUri = new StringBuilder(
                string.Format("{0}?AppId={1}&sources={2}&Query={3}",
                    URIs.Bing_HTTP_Get_XML,
                    AppId,
                    SearchSources,
                    SearchQuery.PrepareStringForSearch()
                    )
                );
            if ((Params != null) && (Params.Count > 0))
            {
                foreach (KeyValuePair<string, object> entry in Params)
                {
                    sbFullApiUri.Append(string.Format("&{0}={1}", entry.Key, entry.Value.ToString()));
                }
            }
            //BMK Execute web request to Bing
            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(sbFullApiUri.ToString());
            HttpWebResponse webResponse = null;
            XDocument xdocResult = null;
            try
            {
                webResponse = (HttpWebResponse)webRequest.GetResponse();
                xdocResult = XDocument.Load(webResponse.GetResponseStream());
            }
            catch //BMK catch connection error  => maybe the connection is not available. check different types of exception
            {

            }
            return xdocResult;
        }

        internal static SearchResult DoSearch(SearchRequestBase search)
        {
            XDocument xdocResult = DoSearch(
                                        search.AppId,
                                        search.Sources,
                                        search.Query,
                                        search.Offset,
                                        search.Count
                                        );
            SearchResult Result = new SearchResult(xdocResult);
            return Result;
        }
        #endregion

        public static SearchResultBase ExecuteSearch(string AppID, SearchSource searchTypeEnum, List<string> locations)
        {
            // XDocument doSearch = DoSearch(searchTypeEnum,
            throw new NotImplementedException();
        }
        public static SearchResult ExecuteSearch(SearchRequestBase search)
        {
            return DoSearch(search);
        }
    }
}
